/* 初始化事件 */
export function initEvents (vm: Component) {
	// 在vm上创建一个
	vm._events = Object.create(null)
	// 这个bool标志位来表明是否存在钩子，而不需要通过哈希表的方法来查找是否有钩子，这样做可以减少不必要的开销，优化性能。
	vm._hasHookEvent = false
	// 初始化父组件attach的事件
	const listeners = vm.$options._parentListeners
	if (listeners) {
		updateComponentListener(vm, listeners)
	}
} 

// $on
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
	const vm: Component = this
	// 如果是数组的时候，则递归$on，为每一个成员都绑定上方法
	if (Array.isArray(event)) {
		for (let i = 0, len = event.length; i < len; i++) {
			this.$on(event[i], fn)
		}
	} else {
		(vm._events[event] || (vm.events[event] = [])).push(fn)
		// optimize hook:event cost by using a boolean flag marked at registration
    // instead of a hash lookup
    // 这里在注册事件的时候标记bool值也就是个标志位来表明存在钩子，而不需要通过哈希表的方法来查找是否有钩子，这样做可以减少不必要的开销，优化性能
    if (hookRE.test(event)) {
    	vm._hasHookEvent = true
    }
	}
	return vm
}

// $once
Vue.prototype.$once = function (event: string, fn: Function): Component {
	const vm: Component = this
	function on () {
		// 在第一次执行的时候将该事件销毁
		vm.$off(event, on)
		// 执行注册的方法
		fn.apply(vm, arguments)
	}
	on.fn = fn
	vm.$on(event, on)
	return vm
}

// $off
Vue.prototype.$off = function(event?:string | Array<string>, fn?: Function): Component {
	const vm: Component = this
	// all
	// 如果不传参数则注销所有事件
	if (!arguments.length) {
		vm._events = Object.create(null)
		return vm
	}
	// array of events
	// 如果event是数组则递归注销事件
	if (Array.isArray(event)) {
		for (let i = 0, len = event.length; i < len; i++) {
			this.$off(even[i], fn)
		}
		return vm
	}
	// specific event
	const cbs = vm._events[event]
	// 本身不存在该事件则直接返回
	if (!cbs) {
		return vm
	}
	// 如果只传了event参数则注销该event方法下的所有方法
	if (arguments.length === 1) {
		vm._events[event] = null
		return vm
	}
	// specific handler
	// 遍历寻找对应方法并删除
	let cb
	let i = cbs.length
	while (i--) {
		cb = cbs[i]
		if (cb === fn || cb,.fn === fn) {
			cbs.splice(i, 1)
			break
		}
	}
	return vm

}